from Interpreter import Interpreter
from TokenType import TokenType
from Lexer import Lexer
import Ast


class Parser:
    __lexer = None
    __current_token = None

    def __init__(self, expression: str):
        self.__lexer = Lexer(expression)
        token = self.__lexer.get_next_token()
        self.__current_token = token

    def error(self):
        raise Exception('Invalid syntax')

    def eat(self, token_type: TokenType):
        if self.__current_token.get_type() == token_type:
            self.__current_token = self.__lexer.get_next_token()
        else:
            self.error()

    def level4(self):
        """factor : INTEGER | LPAREN expr RPAREN"""
        token = self.__current_token
        if token.type == TokenType.NUMBER:
            self.eat(TokenType.NUMBER)
            return Ast.NumAst(token)
        if token.type == TokenType.VARIABLE:
            self.eat(TokenType.VARIABLE)
            return Ast.VariableAst(token)
        if token.type == TokenType.MINUS:
            self.eat(TokenType.MINUS)
            node = self.level4()
            return Ast.NegativeAst(node)
        if token.type == TokenType.LEFT_BRACKET:
            self.eat(TokenType.LEFT_BRACKET)
            node = self.level1()
            self.eat(TokenType.RIGHT_BRACKET)
            return node

    def level3(self):
        """term : factor ((MUL | DIV) factor)*"""
        node = self.level4()

        while self.__current_token.get_type() == TokenType.POWER:
            token = self.__current_token
            self.eat(TokenType.POWER)
            node = Ast.BinOpAst(node, token, self.level4())
        return node

    def level2(self):
        """term : factor ((MUL | DIV) factor)*"""
        node = self.level3()

        while self.__current_token.get_type() in (TokenType.MULTIPLY, TokenType.DIVIDE):
            token = self.__current_token
            if token.type == TokenType.MULTIPLY:
                self.eat(TokenType.MULTIPLY)
            elif token.type == TokenType.DIVIDE:
                self.eat(TokenType.DIVIDE)
            node = Ast.BinOpAst(node, token, self.level3())
        return node

    def level1(self):
        """expr   : term ((PLUS | MINUS) term)*"""
        node = self.level2()

        while self.__current_token.get_type() in (TokenType.PLUS, TokenType.MINUS):
            token = self.__current_token
            if token.get_type() == TokenType.PLUS:
                self.eat(TokenType.PLUS)
            elif token.get_type() == TokenType.MINUS:
                self.eat(TokenType.MINUS)
            node = Ast.BinOpAst(node, token, self.level2())
        return node

    def parsing(self) -> Interpreter:
        return Interpreter(self.level1())
